﻿using System;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows.Controls;
using CredentialsManager.Properties;
using MVVMFoundation;

namespace CredentialsManager.ViewModels
{
    public class UserViewModel : ViewModelBase
    {
        #region Private member variables

        private bool m_IsApproved;
        private bool m_IsUserNameEditable;
        private string m_Application;
        private string m_UserName;
        private string m_Password;
        private string m_CheckPassword;
        private string m_Email;
        private string m_SecurityQuestion;
        private string m_SecurityAnswer;
        private ValidationResult m_PasswordValidity;
        private ValidationResult m_CheckPasswordValidity;
        private PasswordSettings m_PasswordSettings;
        private bool m_IsValid;

        #endregion

        #region Constructors

        public UserViewModel(string displayName, string application, PasswordSettings passwordSettings)
        {
            if (string.IsNullOrEmpty(application) ||
                application.Trim() == string.Empty)
            {
                throw new ArgumentOutOfRangeException("application");
            }

            if (passwordSettings == null)
            {
                throw new ArgumentNullException("passwordSettings");
            }

            this.Application = application;
            this.DisplayName = displayName;
            m_PasswordSettings = passwordSettings;
            this.IsUserNameEditable = true;
            this.PasswordValidity = GetPasswordValidity();
            this.CheckPasswordValidity = GetCheckPasswordValidity();
        }

        #endregion

        #region Public properties

        public bool IsApproved
        {
            get
            {
                return m_IsApproved;
            }
            set
            {
                m_IsApproved = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
            }
        }

        public bool IsUserNameEditable
        {
            get
            {
                return m_IsUserNameEditable;
            }
            protected set
            {
                m_IsUserNameEditable = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
            }
        }

        public string Application
        {
            get
            {
                return m_Application;
            }
            private set
            {
                m_Application = value;
               RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
            }
        }

        public string UserName
        {
            get
            {
                return m_UserName;
            }
            set
            {
                if (!this.IsUserNameEditable)
                {
                    throw new InvalidOperationException(Resources.UserNameIsNotEditableErrorMessage);
                }

                m_UserName = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
                Validate();
            }
        }

        public string Password
        {
            get
            {
                return m_Password;
            }
            set
            {
                m_Password = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
                this.PasswordValidity = GetPasswordValidity();
                Validate();
            }
        }

        public ValidationResult PasswordValidity
        {
            get
            {
                return m_PasswordValidity;
            }
            private set
            {
                m_PasswordValidity = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
            }
        }

        public string CheckPassword
        {
            get
            {
                return m_CheckPassword;
            }
            set
            {
                m_CheckPassword = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
                this.CheckPasswordValidity = GetCheckPasswordValidity();
                Validate();
            }
        }

        public ValidationResult CheckPasswordValidity
        {
            get
            {
                return m_CheckPasswordValidity;
            }
            private set
            {
                m_CheckPasswordValidity = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
            }
        }

        public bool RequiresEmail
        {
            get
            {
                return m_PasswordSettings.RequiresUniqueEmail;
            }
        }

        public string Email
        {
            get
            {
                return m_Email;
            }
            set
            {
                m_Email = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
                Validate();
            }
        }

        public bool RequiresQuestionAndAnswer
        {
            get
            {
                return m_PasswordSettings.RequiresQuestionAndAnswer;
            }
        }

        public string SecurityQuestion
        {
            get
            {
                return m_SecurityQuestion;
            }
            set
            {
                m_SecurityQuestion = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
                Validate();
            }
        }

        public string SecurityAnswer
        {
            get
            {
                return m_SecurityAnswer;
            }
            set
            {
                m_SecurityAnswer = value;                
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
                Validate();
            }
        }

        public bool IsValid
        {
            get
            {
                return m_IsValid;
            }
            protected set
            {
                m_IsValid = value;
                RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
            }
        }

        #endregion

        #region Protected properties

        protected PasswordSettings PasswordSettings
        {
            get
            {
                return m_PasswordSettings;
            }
        }

        #endregion

        #region Protected methods

        protected virtual void Validate()
        {
            this.IsValid = true;
        }        

        protected string ConvertStatusCodeToErrorMessage(MembershipCreateStatus status)
        {
            switch (status)
            {
                case MembershipCreateStatus.DuplicateEmail:
                    return Resources.MembershipCreateStatus_DuplicateEmail_Message;
                case MembershipCreateStatus.DuplicateUserName:
                    return Resources.MembershipCreateStatus_DuplicateUserName_Message;
                case MembershipCreateStatus.InvalidAnswer:
                    return Resources.MembershipCreateStatus_InvalidAnswer_Message;
                case MembershipCreateStatus.InvalidEmail:
                    return Resources.MembershipCreateStatus_InvalidEmail_Message;
                case MembershipCreateStatus.InvalidPassword:
                    return Resources.MembershipCreateStatus_InvalidPassword_Message;
                case MembershipCreateStatus.InvalidQuestion:
                    return Resources.MembershipCreateStatus_InvalidQuestion_Message;
                case MembershipCreateStatus.InvalidUserName:
                    return Resources.MembershipCreateStatus_InvalidUserName_Message;
                case MembershipCreateStatus.ProviderError:
                    return Resources.MembershipCreateStatus_ProviderError_Message;
                case MembershipCreateStatus.UserRejected:
                    return Resources.MembershipCreateStatus_UserRejectedMessage;
                case MembershipCreateStatus.Success:
                    return null;
                default:
                    return Resources.MembershipCreateStatus_Unhandled_Message;
            }
        }

        #endregion

        #region Private methods

        private ValidationResult GetCheckPasswordValidity()
        {
            if (this.Password != this.CheckPassword &&
               this.PasswordValidity.IsValid)
            {
                return new ValidationResult(false, Resources.PasswordsDoNotMatchErrorMessage);
            }

            return new ValidationResult(true, null);
        }

        private ValidationResult GetPasswordValidity()
        {
            if (string.IsNullOrEmpty(this.Password))
            {
                return new ValidationResult(false, Resources.PasswordNullOrEmptyErrorMessage);
            }

            string nonANchars = Regex.Replace(this.Password, "[^a-zA-Z0-9]", "");

            if (nonANchars.Length < m_PasswordSettings.MinRequiredNonAlphanumericCharacters)
            {
                return new ValidationResult(false, string.Format(
                    Resources.PasswordNonAlphanumericCharactersError, nonANchars.Length.ToString(),
                    m_PasswordSettings.MinRequiredNonAlphanumericCharacters.ToString()));
            }

            if (this.Password.Length < m_PasswordSettings.MinRequiredPasswordLength)
            {
                return new ValidationResult(false, string.Format(
                    Resources.PasswordMinimumLengthError,
                    m_PasswordSettings.MinRequiredPasswordLength.ToString()));
            }

            if (!string.IsNullOrEmpty(m_PasswordSettings.PasswordStrengthRegularExpression) &&
                m_PasswordSettings.PasswordStrengthRegularExpression.Trim() != string.Empty &&
                !Regex.IsMatch(this.Password, m_PasswordSettings.PasswordStrengthRegularExpression))
            {
                return new ValidationResult(false, string.Format(
                    Resources.PasswordRegularExpressionError,
                    m_PasswordSettings.PasswordStrengthRegularExpression));
            }

            return new ValidationResult(true, null);
        }

        #endregion
    }
}